<?php
/**
 * Created by PhpStorm.
 * User: OwenZhang
 * Date: 2019/7/1
 * Time: 上午8:28
 */

namespace app\common;

use app\lucky\user\model\UserV2Model;
use think\App;
use think\Controller;
use think\Request;

class BaseController extends Controller
{
    protected $request;

    protected $authCheck = true;
    protected $headerCheck = true;

    protected $loginInfo = [];
    protected $originHeaderPrefix = "";
    protected $requestLastIp = "";

    public function __construct(App $app = null, Request $request = null)
    {
        parent::__construct($app);

        $request = $this->app->request;
        $this->request = $request;
        $this->requestLastIp = $request->ip();
        // options 请求直接终止
        if ($this->request->method() == 'OPTIONS') {
            exit();
        }

        // 头部校验
        $path = $this->request->path();
        //访问频率限制
        $this->accessInterval($path);
        // 登录及权限校验
        if ($this->authCheck) {
            if (!in_array($path, $this->authExceptPath)) {
                $userId = $this->checkLogin();
                $this->checkAuth($userId, $path);
            }
        }
    }

    //需要添加用户访问时间限制的路由
    protected $accessIntervalPath = [
        //发布晒票
        'sp/showticket/add' => [
            'interval_time' => 5 //间隔时间
        ]
    ];

    protected $HeaderExceptPath = [

    ];

    /*
     * 不用登录路由
     */
    protected $authExceptPath = [
        'sports/blog/other/list',
        'sports/follow/other/list/user',
        'sports/follow/my/fans'
    ];

    /**
     * 仅限自己操作
     */
    protected $selfPath = [
    ];

    /**
     * (废弃)
     *
     * @return      bool|mixed
     * @description token验证， 验证通过返回token中保存的 用户ID
     */
    public function checkLogin()
    {
        $header = $this->request->header();

        //非正常环境模拟登陆
        if (env('APP_ENV') != 'prod' && isset($header['mock-user-id'])) {
            $this->loginInfo = ['user_id' => $header['mock-user-id']];
            return $header['mock-user-id'];
        }

        $token = isset($header["auth-token"]) ? $header["auth-token"] : '';
        $sys = session('sys') ?: 343;
        if (!$token) {
            if ($sys == 343) {
                $this->_error(_MSG_INVALID_ACCESS_TOKEN, "无效Token");
            } else {
                $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], '无效Token');
            }
        }

        $userId = Redis::getInstance()->redisGet(_USER_TOKEN_LOGIN . $token);
        if (empty($userId)) {
            if ($sys == 343) {
                $this->_error(_MSG_INVALID_ACCESS_TOKEN, "Token过期");
            } else {
                $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], 'Token过期');
            }
        }

        $loginId = JWT::getInstance()->checkTokenJWT($token);
        if (!$loginId) {
            if ($sys == 343) {
                $this->_error(_MSG_INVALID_ACCESS_TOKEN, "Token过期");
            } else {
                $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], 'Token过期');
            }
        }

        $userIdToken = Redis::getInstance()->redisGet(_USER_TOKEN_LOGIN . $loginId);
        if ($userIdToken['data'] != $token) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], '您的账号在另一台设备上登录，请重新登录');
        }

        Redis::getInstance()->redisSet(_USER_TOKEN_LOGIN . $loginId, $token);
        Redis::getInstance()->redisSet(_USER_TOKEN_LOGIN . $token, $loginId);

        $loginInfo = UserV2Model::getInstance()->getUserByUserId($loginId, $sys);
        if ($loginInfo["code"] != _MSG_SYSTEM_SUCCESS) {
            if ($sys == 343) {
                $this->_error(_MSG_INVALID_ACCESS_TOKEN, "用户不存在");
            } else {
                $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], '用户不存在');
            }
        }

        $this->loginInfo = [
            "user_id" => $loginInfo["data"]["id"],
            "nickname" => $loginInfo["data"]["nickname"],
            "email" => $loginInfo["data"]["email"],
            "tel" => $loginInfo["data"]["tel"],
            "headimgurl" => $loginInfo["data"]["headimgurl"]
        ];

        return $loginId;
    }

    /**
     * 新版本用
     */
    public function checkLoginV2()
    {
        $sys = session('sys') ?: 'sports';
        $header = $this->request->header();
        //非正常环境模拟登陆
        if (env('APP_ENV') != 'prod' && isset($header['mock-user-id'])) {
            $this->loginInfo = ['user_id' => $header['mock-user-id']];
            return $header['mock-user-id'];
        }

        $token = isset($header["auth-token"]) ? $header["auth-token"] : '';

        if (!$token) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], "请先登录");
        }

        $userId = Redis::getInstance()->redisGet(_USER_TOKEN_LOGIN . $token);
        if (empty($userId['data'])) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], "Token过期");
        }

        $loginId = JWT::getInstance()->checkTokenJWT($token);
        if (!$loginId) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], "登录过期，请重新登录");
        }

        $userIdToken = Redis::getInstance()->redisGet(_USER_TOKEN_LOGIN . $loginId);
        if ($userIdToken['data'] != $token) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], '您的账号在另一台设备上登录, 请重新登录');
        }

        Redis::getInstance()->redisSet(_USER_TOKEN_LOGIN . $loginId, $token);
        Redis::getInstance()->redisSet(_USER_TOKEN_LOGIN . $token, $loginId);

        $loginInfo = UserV2Model::getInstance()->getUserByUserId($loginId, $sys);
        if ($loginInfo["code"] != _MSG_SYSTEM_SUCCESS) {
            $this->_jsonReturnV2(_MSG_INVALID_ACCESS_TOKEN, [], "用户不存在");
        }

        $this->loginInfo = [
            "user_id" => $loginInfo["data"]["id"],
            "nickname" => $loginInfo["data"]["nickname"],
            "email" => $loginInfo["data"]["email"],
            "tel" => $loginInfo["data"]["tel"],
            "headimgurl" => $loginInfo["data"]["headimgurl"]
        ];

        return $loginId;
    }


    public function checkLoginUser()
    {
        $header = $this->request->header();

        //非正常环境模拟登陆
        if (env('APP_ENV') != 'prod' && isset($header['mock-user-id'])) {
            $this->loginInfo = ['user_id' => $header['mock-user-id']];
            return ['user_id' => $header['mock-user-id']];
        }

        $token = isset($header["auth-token"]) ? $header["auth-token"] : '';
        if (!$token) {
            return [];
        }

        $userId = Redis::getInstance()->redisGet(_USER_TOKEN_LOGIN . $token);
        if (!$userId["data"] || empty(trim($userId["data"])) || empty($userId["data"]) || is_null($userId["data"])) {
            return [];
        }

        $loginId = JWT::getInstance()->checkTokenJWT($token);
        if (!$loginId) {
            return [];
        }

        $sys = session('sys') ?: 'sports';
        $loginInfo = UserV2Model::getInstance()->getUserByUserId($loginId, $sys);
        if ($loginInfo["code"] != _MSG_SYSTEM_SUCCESS) {
            return [];
        }

        $this->loginInfo = [
            "user_id" => $loginInfo["data"]["id"],
            "nickname" => $loginInfo["data"]["nickname"],
            "email" => $loginInfo["data"]["email"],
            "tel" => $loginInfo["data"]["tel"],
            "headimgurl" => $loginInfo["data"]["headimgurl"]
        ];

        return $this->loginInfo;
    }

    /**
     * @param       $userId
     * @param       $path
     * @return      bool
     * @description 校验权限
     */
    private function checkAuth($userId, $path)
    {
        // 仅限自己操作
        if (in_array($path, $this->selfPath)) {
            if (($this->request->param("user_id")) != $userId . "") {
                $this->_error(_MSG_NOT_ALLOWED_OPERATION, "无权操作");
            }
        }

        return true;
    }

    protected function _error($errorCode, $error, array $response = [])
    {
        $this->_output($errorCode, "error", $error, $error, $response);
    }

    protected function _success(array $response = [], $details = "success")
    {
        $this->_output(_MSG_SUCCESS, "success", lang(0), $details, $response);
    }

    protected function _jsonReturn(array $response = [], $object = false)
    {
        header('Content-type:application/json;charset=utf-8');
        \Sentry\captureMessage(json_encode($response, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));

        exit(json_encode($response, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));
    }

    protected function _jsonReturnV2($code, $data = [], $msg = '', $option = [])
    {
        header('Content-type:application/json;charset=utf-8');
        if (empty($data) && !isset($option['is_array'])) {
            $data = new \stdClass;
        }
        $response = [
            'code' => $code,
            'data' => $data,
            'msg' => $msg
        ];

        $response = $this->htmlDecode($response);
        \Sentry\captureMessage(json_encode($response, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));
        exit(json_encode($response, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));
    }

    protected function _output($errorCode, $status, $msg, $details, array $response = [])
    {
        $result = [
            'meta' => [
                'code' => (int)$errorCode,
                'status' => (string)$status,
                'msg' => (string)$msg,
                'details' => (string)$details,
            ],
            'response' => $response ? $response : new \stdClass()
        ];

        $result = $this->htmlDecode($result);
        \Sentry\captureMessage(json_encode($result, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));
        header('Content-type:application/json;charset=utf-8');
        exit(json_encode($result, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE));
    }

    protected function htmlDecode($data)
    {
        if (is_array($data)) {
            foreach ($data as $key => $value) {
                $data[$key] = $this->htmlDecode($value);
            }
        } elseif (is_string($data)) {
            $data = html_entity_decode($data);
        }

        return $data;
    }

    protected function doGet($url, $urlParams = [], $headers = [], $options = [])
    {
        $response = \app\common\Http::request($url, $headers, $urlParams, 'GET', $options);

        return $response->body;
    }

    /**
     * 访问频率限制
     * @param $userId
     * @param $path
     */
    protected function accessInterval($path)
    {
        if (empty($this->accessIntervalPath[$path])) {
            return [];
        }
        $header = $this->request->header();
        if (empty($header['auth-token'])) {
            return [];
        }
        $res = Redis::getInstance()->redisSetNxTime('access_interval:'.$path.':'.$header['auth-token'], 1, $this->accessIntervalPath[$path]['interval_time']);
        if (!$res) {
            $this->_jsonReturnV2(_MSG_SYSTEM_ERROR, [], "操作过于频繁");
        }
    }
}
